<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript" src="../jsunit/app/jsUnitCore.js"></script>
    <script type="text/javascript" src="../../src/objx.test.js"></script>
    <script type="text/javascript" src="../../src/objx.js"></script>
    <script type="text/javascript">
    
    	function test_getDefinitionObject()
    	{
    	
    		var def = {
    			getName: objx.method()
    		};
    	
            var IContact = objx.interface(def);
            
            assertEquals("Definition of the interface was not as expected.", def, IContact.def);
    	
    	}

        function test_exceptionThrown_whenCreatingAClassThatDoesntMatchTheInterface()
        {
        
            var IContact = objx.interface({
                getName: objx.method()
            });
            
            assertThrowsType("An exception is expected because the class being defined doesn't meet the criteria specified by the interface.", 
                objx.exceptions.DefinitionException, function(){
                    var myClass = objx.Class(IContact, {
                    });
            });
        
        }

        function test_interfacesIsStoredInsideClasses()
        {
        
            var IContact = objx.interface({
                getName: objx.method()
            });
            
            var myClass = objx.Class(IContact, {
                getName: function(){}
            });
            
            var myInst = new myClass();
            
            assertNotUndefined("When a class is defined with interfaces, the interface class should be added to a .interfaces[] array in the prototype.", myInst.type.interfaces);
            assertEquals(IContact, myInst.type.interfaces[0]);
            assertEquals(IContact, myClass.interfaces[0]);
            
            var mySecondInst = new myClass();
            assertEquals(IContact, mySecondInst.type.interfaces[0]);
        
        }
   
        function test_implements()
        {
        
            var IContact = objx.interface({
                getName: objx.method()
            });

            var myClass = objx.Class(IContact, {
                getName: function(){
                    return "Mat";
                }
            });
            
            var anotherClass = objx.Class({
                wrongMethod: function(){}
            });
            
            var thirdClass = objx.Class({
                getName: function(){
                    return "Laurie";
                }
            });
            
            var myClassInstance = new myClass();
            
            assertEquals("Static method and class method implements() returns different results. What's going on?", 
                objx.implements(anotherClass, IContact), anotherClass.implements(IContact));
            assertEquals("Static method and class method implements() returns different results. What's going on?", 
                objx.implements(myClass, IContact), myClass.implements(IContact));
            assertEquals("Static method and class method implements() returns different results. What's going on?", 
                objx.implements(myClassInstance, IContact), myClassInstance.implements(IContact));
            assertEquals("Static method and class method implements() returns different results. What's going on?", 
                objx.implements(thirdClass, IContact), thirdClass.implements(IContact));
            
            assertFalse("class method thirdClass.implements", thirdClass.implements(IContact));
            assertFalse("class method anotherClass.implements", anotherClass.implements(IContact));
            assertTrue("class method myClass.implements", myClass.implements(IContact));
            assertTrue("class method myClassInstance.implements", myClassInstance.implements(IContact));
            
            assertFalse("objx.implements(thirdClass", objx.implements(thirdClass, IContact));
            assertFalse("objx.implements(anotherClass", objx.implements(anotherClass, IContact));
            assertTrue("objx.implements(myClass", objx.implements(myClass, IContact));
            assertTrue("objx.implements(myClassInstance", objx.implements(myClassInstance, IContact));
        
        }
      
        function test_actsAs()
        {
        
            var IContact = objx.interface({
                getName: objx.method()
            });

            var myClass = objx.Class(IContact, {
                getName: function(){
                    return "Mat";
                }
            });
            
            var anotherClass = objx.Class({
                wrongMethod: function(){}
            });
            
            var thirdClass = objx.Class({
                getName: function(){
                    return "Laurie";
                }
            });
            
            var myClassInstance = new myClass();
            
            // make sure the class method returns the same value as the static method
            assertEquals("Static method and class method actsAs() returns different results. What's going on?", 
                objx.actsAs(anotherClass, IContact), anotherClass.actsAs(IContact));
            assertEquals("Static method and class method actsAs() returns different results. What's going on?", 
                objx.actsAs(myClass, IContact), myClass.actsAs(IContact));
            assertEquals("Static method and class method actsAs() returns different results. What's going on?", 
                objx.actsAs(myClassInstance, IContact), myClassInstance.actsAs(IContact));
            assertEquals("Static method and class method actsAs() returns different results. What's going on?", 
                objx.actsAs(thirdClass, IContact), thirdClass.actsAs(IContact));
            
            assertFalse("Expected actsAs() to be false since 'anotherClass' doesn't correctly implement 'IContact' interface.", objx.actsAs(anotherClass, IContact));
            assertTrue("Expected actsAs() to be true since 'myClass' correctly implements 'IContact' interface.", objx.actsAs(myClass, IContact));
            assertTrue("Expected actsAs() to be true since 'myClassInstance' correctly implements 'IContact' interface.", objx.actsAs(myClassInstance, IContact));
            assertTrue("Expected actsAs() to be true since 'thirdClass' happens to correctly implement 'IContact' interface - even though it wasn't defined to.", objx.actsAs(thirdClass, IContact));
            
            // test the class methods too
            assertFalse("Expected class method actsAs() to be false since 'anotherClass' doesn't correctly implement 'IContact' interface.", anotherClass.actsAs(IContact));
            assertTrue("Expected class method actsAs() to be true since 'myClass' correctly implements 'IContact' interface.", myClass.actsAs(IContact));
            assertTrue("Expected class method actsAs() to be true since 'myClassInstance' correctly implements 'IContact' interface.", myClassInstance.actsAs(IContact));
            assertTrue("Expected class method actsAs() to be true since 'thirdClass' happens to correctly implement 'IContact' interface - even though it wasn't defined to.", thirdClass.actsAs(IContact));
            
        }

        function test_exceptionThrown_whenCreatingAnInterfaceWithNoDefinition()
        {
        
            assertThrowsType("InterfaceException expected when creating an interface with null argument.", 
                objx.exceptions.ArgumentException, function(){
                    
                    var i = new objx.Interface(null);
                
                }
            );
        
        }
      
        function test_inlineInterfaceDefinition()
        {
        
            var RealInterface = objx.i({
                getData: objx.m(),
                hasData: objx.m()
            });
            
            var InlineDefinedInterface = objx.i("getData", "hasData");
        
            assertNotUndefined(InlineDefinedInterface.def.getData);
            assertNotUndefined(InlineDefinedInterface.def.hasData);
        
            assertEquals(RealInterface.def.getData.toString(), InlineDefinedInterface.def.getData.toString());
            assertEquals(RealInterface.def.hasData.toString(), InlineDefinedInterface.def.hasData.toString());
        
        }
          
        function test_interfaceMethodWithArgumentCount()
        {
        
            var IContact = objx.i({
                getData: objx.m(),
                setData: objx.m(1),
                setName: objx.m(2)
            });
            
            assertNotThrows("Defining a class that meets the interface definition should not throw an exception", 
                function(){
                
                var ValidClass = objx.c(IContact, {
                    getData: function(){},
                    setData: function(d){},
                    setName: function(first, last){}
                });
                
            });
            
            assertThrowsType("Defining a class that does not meet the interface definition should throw an exception.", 
                objx.exceptions.DefinitionException, function(){
                
                    var InvalidClass = objx.c(IContact, {
                        getData: function(){},
                        setData: function(one, two){},
                        setName: function(first, middle, last){}
                    });
                
                }
            );
        
        }
        
        
        
        function test_interfaceMethodWithZeroArgument()
        {
        
            var IContact = objx.i({
                getData: objx.m(0)
            });
            
            assertNotThrows("Method(0) - Defining a class that meets the interface definition should not throw an exception", 
                function(){
                
	                var ValidClass = objx.c(IContact, {
	                    getData: function(){}
	                });
                
            });
            
            assertThrowsType("Method(0) - Defining a class that does not meet the interface definition should throw an exception.", 
                objx.exceptions.DefinitionException, function(){
                
                    var InvalidClass = objx.c(IContact, {
                        getData: function(anArg){}
                    });
                
                }
            );
        
        }
        

        function test_interfaceMethodWithNamedArguments()
        {
        
            var IContact = objx.i({
                getData: objx.m(0),
                setName: objx.m("first", "middle", "last")
            });
            
            assertNotThrows("Method('first', 'middle', 'last') - Defining a class that meets the interface definition should not throw an exception", 
                function(){
	                
	                var ValidClass = objx.c(IContact, {
	                    getData: function(){},
	                    setName: function(first, middle, last){}
	                });
	                
            });
            
            assertThrowsType("Method('first', 'middle', 'last') - Defining a class that does not meet the interface definition should throw an exception.", 
                objx.exceptions.DefinitionException, function(){
            
	                var InvalidClass = objx.c(IContact, {
	                    getData: function(){},
	                    setName: function(firstName, middleName, lastName){}
	                });
            
            });
            
        }
          
        
        function test_interfaceDefinedWithEvent()
        {
        
        	var IContact = objx.i({
        		
        		changeData: Method(1),
        		onDataChanged: Event()
        		
        	});
        	
            assertThrowsType("Defining a class that does not provide a required event should throw an exception.", 
                objx.exceptions.DefinitionException, function(){
            
	                var InvalidClass = objx.c(IContact, {
	                    changeData: function(d){}
	                });
            
            });
            
            assertNotThrows("Defining a class that does provide a required event should not throw an exception.", 
                function(){
                
	                var ValidClass = objx.c(IContact, {
	                    changeData: function(d){
	                    	this.onDataChanged(d);
	                    },
	                    onDataChanged: Event()
	                });
                
            });
        
        }
    
        function test_actsAs_interfaceWithEvents()
        {

        	var IContact = objx.i({
        		
        		changeData: Method(1),
        		onDataChanged: Event()
        		
        	});
        	
			var InvalidClass = objx.c({
			   	changeData: function(d){}
			});
			
			var ValidClass = objx.c({
			    changeData: function(d){
			    	this.onDataChanged(d);
			    },
			    onDataChanged: Event()
			});
			
        	assertFalse("InvalidClass should NOT be seen to actAs an interface with Events when the events are missing from InvalidClass.", InvalidClass.actsAs(IContact));
        	assertTrue("ValidClass should be seen to actAs an interface with Events when the events are present in ValidClass.", ValidClass.actsAs(IContact));
        
        }
        
        function test_toEnsureImplementingAnInterfaceDoesntCarryInitMethod(){
        
        	var BaseOne = AbstractClass({
        		somethingElse: function(){}
        	});
        
        	var IOne = Interface({
        		something: Method()
        	});
        	
        	var COne = Class(BaseOne, IOne, {
        		something: function(){}
        	});

        	assertNotEquals(IOne.init, COne.prototype.init);
        
        	assertEquals(objx.nf.toString(), COne.prototype.init.toString());
        
        }
        
        function test_createClassWithUndefinedInterfaces(){
        
        	/*
        	 *  Undefined interfaces are supported because in 'release' mode
        	 *  there is often no need to use the interfaces, since all tests
        	 *  passing in normal mode usually means that all classes are
        	 *  properly defined.
        	 *
        	 *  This test ensures that passing an undefined item to the Class
        	 *  method doesn't break anything.
        	 *
        	 */
        
        	var TestNamespace = {};
        	TestNamespace.TestSubNamespace = {};
        	
        	var MyClass = Class(TestNamespace.TestSubNamespace.NoSuchInterface, {
        	
        		shout: function(){
        			return "This is Sparta!";
        		}
        	
        	});
        	
        	assertNotUndefined("MyClass", MyClass);
        
        	var myInstance = new MyClass();
        	
        	assertEquals("This is Sparta!", myInstance.shout());
        
        }
        
        function test_interfacesSupportProperties(){
        
        	var MyInterface = Interface({
        		name: Public,
        		age: ReadOnly,
        		salt: WriteOnly
        	});
        
        	assertNotThrows("Implementing the correct properties on an interface shouldn't cause an exception to be thrown.", function(){
	        	
	        	var MyValidClass = Class(MyInterface, {
	        		name: Public,
	        		age: ReadOnly,
	        		salt: WriteOnly
	        	});
	        
	        });
	        
            assertThrowsType("Defining a class without specifying all the Properties defined in the interface should throw an exception but did not.", 
                objx.exceptions.DefinitionException, function(){
	            
		        	var MyInvalidClass = Class(MyInterface, {
		        		name: Public
		        	});
            
            });
	        
        }
        
    </script>
</head>
<body>
</body>
</html>